package com.meshnetics.gb.stack.mac.type;

import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.ByteUtils;
import com.meshnetics.gb.type.impl.custom.CollectionDataValue;
import com.meshnetics.gb.type.impl.zcl.TypeUInt;

import java.nio.ByteBuffer;

/**
 * List of pending addresses in beacon frame (used in MACBeaconNotifyIndication).
 *
 * @author AKovylin
 */
public class BeaconAddrList extends CollectionDataValue {
    public BeaconAddrList() {
        this(new Short[0], new Long[0]);
    }

    public BeaconAddrList(Short[] shortAddresses) {
        this(shortAddresses, new Long[0]);
    }

    public BeaconAddrList(Long[] longAddresses) {
        this(new Short[0], longAddresses);
    }

    public BeaconAddrList(Short[] shortAddresses, Long[] longAddresses) {
        this(createElements(shortAddresses, longAddresses));
    }

    private static DataValue[] createElements(Short[] shortAddresses, Long[] longAddresses) {
        if (shortAddresses == null) {
            throw new NullPointerException("shortAddresses is null");
        }
        if (longAddresses == null) {
            throw new NullPointerException("longAddresses is null");
        }
        if (shortAddresses.length + longAddresses.length > 7) {
            throw new IllegalArgumentException("not more than 7 addresses allowed in total");
        }
        for (int i = 0; i < shortAddresses.length; i++) {
            if (shortAddresses[i] == null) {
                throw new NullPointerException("shortAddresses[" + i + "] is null");
            }
        }
        for (int i = 0; i < longAddresses.length; i++) {
            if (longAddresses[i] == null) {
                throw new NullPointerException("longAddresses[" + i + "] is null");
            }
        }
        DataValue[] elements = new DataValue[shortAddresses.length + longAddresses.length];
        for (int i = 0; i < shortAddresses.length; i++) {
            elements[i] = TypeUInt.UINT_16.fromObject(shortAddresses[i]);
        }
        for (int i = 0; i < longAddresses.length; i++) {
            elements[shortAddresses.length + i] = TypeUInt.UINT_64.fromObject(longAddresses[i]);
        }
        return elements;
    }

    BeaconAddrList(DataValue[] elements) {
        super(TypeBeaconAddrList.TYPE, elements);
    }

    public PendingAddressSpec deriveSpec() {
        int shortAddresses = 0;
        for (int i = 0; i < getCollectionSize(); i++) {
            if (TypeUInt.UINT_16.equals(getElement(i))) {
                shortAddresses++;
            }
        }
        return new PendingAddressSpec(shortAddresses, getCollectionSize() - shortAddresses);
    }

    public Short[] getShortPendingAddresses() {
        DataValue[] shorts = getElements(TypeUInt.UINT_16);
        Short[] res = new Short[shorts.length];
        for (int i = 0; i < res.length; i++) {
            res[i] = (Short) shorts[i].toObject();
        }
        return res;
    }

    public Long[] getExtendedPendingAddresses() {
        DataValue[] longs = getElements(TypeUInt.UINT_64);
        Long[] res = new Long[longs.length];
        for (int i = 0; i < res.length; i++) {
            res[i] = (Long) longs[i].toObject();
        }
        return res;
    }

    public Object toObject() {
        throw new UnsupportedOperationException("not implemented yet");
        // todo reimplement
    }

    public int getDataSize() {
        int n = 0;
        for (int i = 0; i < getCollectionSize(); i++) {
            n += getElement(i).getDataSize();
        }
        return n;
    }

    public void serialize(ByteBuffer bb) {
        ByteUtils.ensureNotNull(bb);
        for (int i = 0; i < getCollectionSize(); i++) {
            getElement(i).serialize(bb);
        }
    }
}
